package com.tjc.timingwheel;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;

 class SystemTimer implements Timer {


    private String executorName;
    private Long tickMs = 1L;
    private Integer wheelSize = 20;
    private Long startMs = System.currentTimeMillis();
    private final TimingWheel timingWheel;
    private final DelayQueue<TimerTaskList> delayQueue = new DelayQueue<>();
    private final AtomicInteger taskCounter = new AtomicInteger(0);

    public SystemTimer(String executorName) {
        this.executorName = executorName;

        timingWheel = new TimingWheel(tickMs, wheelSize, startMs, taskCounter, delayQueue);
    }

    public SystemTimer(String executorName, Long tickMs, Integer wheelSize) {
        this.executorName = executorName;
        this.tickMs = tickMs;
        this.wheelSize = wheelSize;

        timingWheel = new TimingWheel(tickMs, wheelSize, startMs, taskCounter, delayQueue);
    }

    public SystemTimer(String executorName, Long tickMs, Integer wheelSize, Long startMs) {
        this.executorName = executorName;
        this.tickMs = tickMs;
        this.wheelSize = wheelSize;
        this.startMs = startMs;

        timingWheel = new TimingWheel(tickMs, wheelSize, startMs, taskCounter, delayQueue);
    }

    /**
     * �����̳߳�
     */
    private final ExecutorService taskExecutor = Executors.newFixedThreadPool(1, (runnable) -> new Thread(runnable, "executor-" + executorName));

    /**
     * �����ڹ�ѡʱ�������ݽṹ����
     */
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();


    @Override
    public void add(TimerTask timerTask) {
        readLock.lock();
        try {
            addTimerTaskEntry(new TimerTaskEntry(timerTask, timerTask.getDelayMs() + System.currentTimeMillis()));
        } finally {
            readLock.unlock();
        }
    }

    /**
     * �� Systemtimer �����һ���������񱻰�װΪһ��TimerTaskEntry
     *
     * @param timerTaskEntry timerTaskEntry
     */
    private void addTimerTaskEntry(TimerTaskEntry timerTaskEntry) {
        //���ж��Ƿ������ӽ�ʱ�����У������������ӽ�ȥ���������Ѿ����ڻ�������ȡ����
        // ע�������timingWheel������һ��ʱ���ֵ����ã����Կ��ܴ��ڵݹ����
        if (!timingWheel.add(timerTaskEntry)) {
            // �Ѿ����ڻ�ȡ��
            // ��������ֱ���̳߳��첽ִ�е�
            if (!timerTaskEntry.cancelled()) {
                taskExecutor.submit(timerTaskEntry.timerTask);
            }
        }
    }

    private void addTimerTaskEntry(Object o) {
    }


    @Override
    public Boolean advanceClock(Long timeoutMs) {
        try {
            TimerTaskList bucket = delayQueue.poll(timeoutMs, TimeUnit.MILLISECONDS);
            if (bucket != null) {
                writeLock.lock();
                try {
                    while (bucket != null) {
                        // ����ʱ����
                        timingWheel.advanceClock(bucket.getExpiration());
                        //ѭ��buckekҲ���������б������б�һ����������ӽ�ʱ�����Դ����������߽���ʱ���֣��ѹ��������ҳ���ִ��
                        bucket.flush(this::addTimerTaskEntry);
                        //������Ǵ��ӳٶ���ȡ��bucket��bucket�����ӳ�ʱ��ģ�ȡ�������bucket���ڣ�����ͨ��bucket��ȡ��bucket�����������б�
                        bucket = delayQueue.poll();
                    }
                } finally {
                    writeLock.unlock();
                }
                return Boolean.TRUE;
            } else {
                return Boolean.FALSE;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            return Boolean.FALSE;
        }
    }

    @Override
    public Integer size() {
        return taskCounter.get();
    }

    @Override
    public void shutdown() {
        taskExecutor.shutdown();
    }
}
